package com.wzw.springboot.principle.use.batch;

import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author Wangzhiwen
 */
public class ColumnHandler {

    private static final String TABLE = "{{table}}";

    private static final String COLUMN = "{{column}}";

    private static final String VALUES = "{{values}}";

    private static final String INSERT = "INSERT INTO " + TABLE + "( " + COLUMN + " )" + " VALUES ( " + VALUES + " )";

    private static final Map<String, String> humpFieldsCache = new ConcurrentHashMap<>(64);

    private ColumnHandler() {
    }

    public static String camelToUnderline(String param) {
        if (!StringUtils.hasText(param)) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c) && i > 0) {
                sb.append("_");
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }

    public static String underlineToCamel(String param) {
        if (!StringUtils.hasText(param)) {
            return "";
        }
        String temp = param.toLowerCase();
        int len = temp.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = temp.charAt(i);
            if (c == '_') {
                if (++i < len) {
                    sb.append(Character.toUpperCase(temp.charAt(i)));
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public static String methodToProperty(String name) {
        if (name.startsWith("is")) {
            name = name.substring(2);
        } else if (name.startsWith("get") || name.startsWith("set")) {
            name = name.substring(3);
        } else {
            throw new RuntimeException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
        }

        if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
        }

        return name;
    }

    private static String buildSql(String table, String columns, String values, Boolean isLowerCase, String raw) {
        if (isLowerCase) {
            return raw.replace(TABLE, table.toLowerCase(Locale.ROOT))
                    .replace(COLUMN, columns.toLowerCase(Locale.ROOT))
                    .replace(VALUES, values.toLowerCase(Locale.ROOT));
        }
        return raw.replace(TABLE, table.toUpperCase(Locale.ROOT))
                .replace(COLUMN, columns.toUpperCase(Locale.ROOT))
                .replace(VALUES, values.toUpperCase(Locale.ROOT));
    }

    public static String mappedWithInsert(Class<?> clazz) {
        boolean annotationPresent = clazz.isAnnotationPresent(ColumnMapped.class);
        Field[] declaredFields = clazz.getDeclaredFields();
        List<String> fieldNames = annotationPresent
                ?
                Arrays.stream(declaredFields).map(Field::getName).collect(Collectors.toList())
                :
                Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(ColumnMapped.class)).map(Field::getName).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(fieldNames)) {
            return "";
        }
        String table = camelToUnderline(clazz.getSimpleName());
        return buildSql(
                table,
                fieldNames.stream().map(ColumnHandler::camelToUnderline).collect(Collectors.joining(", ")),
                fieldNames.stream().map(s -> ":" + s).collect(Collectors.joining(", ")),
                true,
                INSERT);

    }

    @Data
    @Accessors(chain = true)
    static class ColumnCache {

        private String fieldName;

        private String columnName;

    }

}
